package eCheque;

import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List;

/**
 * Utility class for registration file IO
 * 
 * @author Steve Pennington
 * 
 */
public class RegistrationUtils {

	private static String CONFIG_FILE_SUFFIX = ".epc";
	private static String CONFIG_FOLDER_PATH = "./";

	private static FileFilter configFileFilter = new ConfigFileFilter();

	/**
	 * Search the filesystem for serialized {@link EChequeRegisteration} objects
	 * and return the deserialized versions.
	 * 
	 * Note: If an IO error occurs while loading a registration file it is
	 * simply skipped. Therefore, there is the potential for successive calls to
	 * return different results without filesystem modification.
	 * 
	 * @return All registration files that could be deserialized.
	 */
	public static List<EChequeRegisteration> getAccounts() {
		List<EChequeRegisteration> accounts = new ArrayList<EChequeRegisteration>();
		File[] configFiles = getConfigFiles();
		for (File f : configFiles) {
			try {
				EChequeRegisteration account = readAccount(f);
				accounts.add(account);
				String desiredFilename = accountToFilename(account);
				if (!f.getName().equals(desiredFilename)) {
					// old config files were named "Config.epc" rename
					// to the new standard
					f.renameTo(new File(desiredFilename));
				}
			} catch (Exception e) {
				// Not actually an account, but made it through the file filter
			}
		}
		return accounts;
	}

	/**
	 * Write the registration information to the filesystem
	 * 
	 * @param account
	 *            Registation information to write
	 * 
	 * @throws DuplicateAccountException
	 *             If a file already exists for the account
	 * @throws IOException
	 *             If there was an error accessing the filesystem
	 */
	public static void writeAccount(EChequeRegisteration account)
			throws DuplicateAccountException, IOException {
		FileOutputStream fileOut = null;
		ObjectOutputStream outObj = null;

		String filename = accountToFilename(account);
		try {
			File outFile = new File(filename);
			if (outFile.exists()) {
				try {
					EChequeRegisteration existingAccount = readAccount(outFile);
					// If we got here then the outFile contained a valid
					// EChequeResiteration
					// which means the user is trying to create a duplicate
					// account
					throw new DuplicateAccountException(existingAccount);
				} catch (IOException e) {
					// we couldn't parse an account from the file
					throw new IOException();
				} catch (ClassCastException e) {
					// Invalid config file
					throw new IOException(e);
				} catch (ClassNotFoundException e) {
					// Invalid config file
					throw new IOException(e);
				}
			}
			fileOut = new FileOutputStream(outFile);
			outObj = new ObjectOutputStream(fileOut);
			outObj.writeObject(account);
			outObj.close();
		} finally {
			try {
				if (outObj != null) {
					outObj.close();
				} else if (fileOut != null) {
					fileOut.close();
				}
			} catch (IOException e) {}
		}
	}

	/**
	 * Remove the registration information from the filesystem
	 * 
	 * @param account
	 *            Registration information to delete
	 * @return <code>true</code> if the deletion was successful, otherwise
	 *         <code>false</code>
	 */
	public static boolean deleteAccount(EChequeRegisteration account) {
		String filename = accountToFilename(account);
		File accountFile = new File(filename);
		return accountFile.delete();
	}

	private static EChequeRegisteration readAccount(File f)
			throws FileNotFoundException, IOException, ClassNotFoundException,
			ClassCastException {
		ObjectInputStream ois = null;
		try {
			ois = new ObjectInputStream(new FileInputStream(f));
			Object obj = ois.readObject();
			return (EChequeRegisteration) obj;
		} catch (IOException e) {
			throw e;
		} finally {
			try {
				if (ois != null) {
					ois.close();
				}
			} catch (IOException e) {}
		}
	}

	private static String accountToFilename(EChequeRegisteration account) {
		return CONFIG_FOLDER_PATH + account.getAccountNumber()
				+ CONFIG_FILE_SUFFIX;
	}

	private static File[] getConfigFiles() {
		File dir = new File(CONFIG_FOLDER_PATH);
		return dir.listFiles(configFileFilter);
	}

	private static class ConfigFileFilter implements FileFilter {

		/*
		 * (non-Javadoc)
		 * 
		 * @see java.io.FileFilter#accept(java.io.File)
		 */
		@Override
		public boolean accept(File f) {
			return f.isFile() && f.getName().endsWith(CONFIG_FILE_SUFFIX);
		}

	}
}
